#include <sys/types.h>
#include<sys/wait.h>
#include <unistd.h>
#include <cassert>
#include <unistd.h>
#include <iostream>
#include <ctime>
#include <cstdio>
#include <cstdlib>
#include <string>
#include <vector>
using namespace std;
#define PRO_NUM 5
#define MakeRandSeed() srand((unsigned long)time(NULL))
///////////////////////task////////////////////////
typedef void (*func_t)();
void DownLoadTask()
{
  cout << "download task" << endl;
}
void IOTask()
{
  cout << "IO task" << endl;
}
void flushTask()
{
  cout << "flush task" << endl;
}
void LoadTaskFunc(vector<func_t> &out)
{
  out.push_back(DownLoadTask);
  out.push_back(IOTask);
  out.push_back(flushTask);
}
////////////////////process////////////////////////////
class SubEndPoint
{
public:
  SubEndPoint(pid_t subId, int writefd)
      : _subId(subId), _writefd(writefd)
  {
    char buffer[1024];
    snprintf(buffer, sizeof(buffer), "process-%d[pid(%d)]", _num++, _subId);
    _name = buffer;
  }

public:
  static int _num;
  string _name;
  int _subId;
  int _writefd;
};

int SubEndPoint::_num = 0;
int recvCommand(int rfd)
{
  int code = 0;
  ssize_t n = read(rfd, &code, sizeof(code));
  //assert(n == sizeof(int));
  if(n == 4)
    return code;
  else if(n == 0)
    return -1;
  else
    return 0;
}
void SendTask(SubEndPoint &sub, int taskNum)
{
  // write in the pipe
  cout << "send task num:" << taskNum << " send to " << sub._name << endl;
  ssize_t n = write(sub._writefd, &taskNum, sizeof(taskNum));
  //assert(n == sizeof(int));
  //cout << n << endl;
  (void)n;
}

void CreatPro(vector<SubEndPoint> &subs, vector<func_t> &funcMap)
{
  // creat child process
  for (int i = 0; i < PRO_NUM; i++)
  {
    int fds[2];
    int n = pipe(fds);
    assert(n == 0);
    pid_t id = fork();
    if (id == 0)
    {
      // child
      close(fds[1]);
      while (true)
      {
        // get a command code , if there are none task , the child process should be hang.
        int commandCode = recvCommand(fds[0]);
        if(commandCode >= 0 && commandCode < funcMap.size()) 
          funcMap[commandCode]();
        else if(commandCode == -1)//quit process
        {
          break;
        }
      }
      exit(0);
    }
    // parent
    close(fds[0]);
    SubEndPoint sub(id, fds[1]);
    subs.push_back(sub);
  }
}
void LoadBlanceControl(vector<SubEndPoint>& subs , vector<func_t>& funcMap , int cntTask)
{
  int cnt_pro = subs.size();
  int cnt_task = funcMap.size();
  bool isforever = (cntTask == 0 ? true : false);
  while (true)
  {
    // choose a child process
    int proIndex = rand() % cnt_pro;
    // choose a task ,what we want the child process run
    int taskIndex = rand() % cnt_task;
    // assemble the task for child process
    SendTask(subs[proIndex], taskIndex);
    if(!isforever && cntTask > 0)
    {
      cout << cntTask << endl;
      cntTask--;
      if(cntTask == 0)
        break;
    }
    sleep(1);
  }
  //close write fd
  for(int i = 0 ; i < cnt_pro ; i++)
    close(subs[i]._writefd);

}
void waitProcess(vector<SubEndPoint>& subs)
{
  int cnt_pro = subs.size();
  for(int i = 0 ;i < cnt_pro ; i++)
  {
    waitpid(subs[i]._subId, NULL , 0);
  }
}
int main()
{
  MakeRandSeed();
  // need remain every process's fd
  vector<SubEndPoint> subs;
  vector<func_t> funcMap;
  LoadTaskFunc(funcMap);
  CreatPro(subs, funcMap);
  int cntTask = 4;
  LoadBlanceControl(subs , funcMap , cntTask);
  waitProcess(subs);
  return 0;
}
